/* syscall.c : syscalls
 */

#include <kernel.h>
#include <stdarg.h>

/* Your code goes here */
extern unsigned int globalPIDcounter;

/* syscall is the generic function which traps to kernel execution and can pass any number of args to it */
extern uint32 syscall ( system_call call, ... ){
	/*  Using stdarg for variable arguments. Keep a pointer to it (that is passed through edx) 
		to get it in the kernel*/
	va_list ap;
	va_start(ap, call);
	va_list* apPtr = &ap;
	if(SYSCALL_UNIT_TEST){
		kprintf("Saving call-enum: %X and va_list* %X in regs for trap passage to disp.\n", call, apPtr);
	}

	/* Store the value of 'call' in eax, so it can be preserved when we switch to kernel space. The trap will pusha. */
	__asm__( "movl %0, %%eax\n"	: /* No output */ : "m" (call) : "%eax");
	/* Put a pointer to the arguments of this function in register edx */
	__asm__( "movl %0, %%edx\n"	: /* No output */ : "m" (apPtr) : "%edx");

	/* Trap into kernel space with 'int'. 0x43 = 67d */
	__asm__( "int $0x43\n");
	
	/* ======= KERNEL EXECUTES SYSCALL, COMES BACK HERE IN USER SPACE WITH IRET ======= */
	
	/* syscall returned result is written to the pushed context 'eax' on the process stack by the dispatcher. 
		Context switching popped it to the CPU with popa. Read it out now and assign it to a C variable that 
		syscall() can return to its caller. */
	uint32 syscallResult = NULL;
	__asm__( "movl %%eax, %0\n"	:"=m" (syscallResult));
	if(SYSCALL_UNIT_TEST){
		kprintf("Syscall result: %d dec or %X hex.\n", syscallResult, syscallResult);
	}

	va_end(ap);

	return syscallResult;
}


// These are userspace functions.
extern int syscreateN ( void (*func)(), int stack, char* name ){
	/* Need to copy the string so that disp() can access it. */
	if(SYSCALL_UNIT_TEST){
		kprintf("syscreate called with functionPtr %X, stackSize %X, name %s.\n", func, stack, name);
		DELAY(SYSCALL_UT_DELAY);
	}
	uint32 syscallResult =  syscall ( SYSCREATE, func, stack, name, NULL );
	if(SYSCALL_UNIT_TEST){
		kprintf("syscreate: Syscall result: %d dec or %X hex.\n", syscallResult, syscallResult);
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscallResult;
}

extern int syscreate ( void (*func)(), int stack ){
	return syscreateN( func, stack, "child" );
}

extern int sysyield ( void ){
	if(SYSCALL_UNIT_TEST){
		kprintf("sysyield called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscall ( SYSYIELD, NULL );
}

extern int sysstop ( void ){
	if(SYSCALL_UNIT_TEST){
		kprintf("sysstop called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscall ( SYSSTOP, NULL );
}

extern void sysputs( char* str ){
	if(SYSCALL_UNIT_TEST){
		kprintf("sysputs called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	syscall (SYSPUTS, str, NULL);
	return;
}
extern int sysgetpid( void ){
	if(SYSCALL_UNIT_TEST){
		kprintf("sysgetpid called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscall (SYSGETPID, NULL);
}

/* Send data to another process. Returns -1 if fail, or the number of bytes sent. 
   Blocks untill the send operation completes. */
extern int syssend( int to, void *buffer, int buffer_len ){
	if(SYSCALL_UNIT_TEST){
		kprintf("syssend called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscall (SYSSEND, to, buffer, buffer_len, NULL);
}

/* 	Recieve data from another process. Blocks untill there is a message to recieve from ANY process. 
	Returns num bytes receieved, else -1 on fail. 
	The 'from' pointer value is changed to identify the sender process. */
extern int sysrecv( int *from, void *buffer, int buffer_len ){
	if(SYSCALL_UNIT_TEST){
		kprintf("sysrecv called\n");
		DELAY(SYSCALL_UT_DELAY);
	}
	return syscall (SYSRECV, from, buffer, buffer_len, NULL);
}


